package cn.sylinx.horm.dialect.fs;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;

import cn.sylinx.horm.core.common.OrderBy;
import cn.sylinx.horm.core.common.TypedParameter;
import cn.sylinx.horm.exception.HORMException;
import cn.sylinx.horm.model.anno.PrimaryKey;
import cn.sylinx.horm.model.cache.ModelCacheUtil;
import cn.sylinx.horm.model.cache.ModelFabric;
import cn.sylinx.horm.model.lambda.TypeFunction;
import cn.sylinx.horm.type.handler.TypeHandler;
import cn.sylinx.horm.util.GLog;
import cn.sylinx.horm.util.LambdaUtil;
import cn.sylinx.horm.util.StrKit;

/**
 * fluent sql support, KEY WORD CAN NOT BE USED!
 * 
 * @author han
 *
 * @param <T>
 */
public class FS<T> {

    public static final String AND_STR = " AND ";
    public static final String BLANK_STR = "";

    protected Class<T> modelClass;
    protected StringBuilder conditionSQL = new StringBuilder();
    protected StringBuilder orderBySQL = new StringBuilder();
    protected StringBuilder groupBySQL = new StringBuilder();
    protected StringBuilder limitSQL = new StringBuilder();
    protected List<String> excludedColumns = new ArrayList<>(8);
    protected List<Object> paramValues = new ArrayList<>(8);
    protected Map<String, Object> updateColumns = new LinkedHashMap<>(8);
    protected Map<String, String> updateColumnExpression = new LinkedHashMap<>(8);
    protected String selectColumns;
    protected String selectExpressionColumns;
    protected boolean distinct = false;
    protected boolean count = false;
    protected String[] primaryKeyColumn;
    protected String tableName;
    protected Map<String, String> attrs = null;
    protected Map<String, TypeHandler<?>> typeHandlerMap = null;
    protected T model;
    protected String preEscape;
    protected String postEscape;
    protected boolean singleField = false;
    protected String hint;

    protected String tablePostfix = BLANK_STR;
    protected String tablePrefix = BLANK_STR;

    public static <T> FS<T> of(Class<T> modelClass) {
        return of(BLANK_STR, modelClass, BLANK_STR);
    }

    public static <T> FS<T> of(Class<T> modelClass, String tablePostfix) {
        return of(BLANK_STR, modelClass, tablePostfix);
    }

    public static <T> FS<T> of(String tablePrefix, Class<T> modelClass) {
        return of(tablePrefix, modelClass, BLANK_STR);
    }

    public static <T> FS<T> of(String tablePrefix, Class<T> modelClass, String tablePostfix) {
        return new FS<T>().init(tablePrefix, modelClass, tablePostfix);
    }

    protected String[] getEscape() {
        return new String[] { BLANK_STR, BLANK_STR };
    }

    public FS<T> init(Class<T> modelClass) {
        return init(BLANK_STR, modelClass, BLANK_STR);
    }

    public FS<T> init(String tablePrefix, Class<T> modelClass) {
        return init(tablePrefix, modelClass, BLANK_STR);
    }

    public FS<T> init(Class<T> modelClass, String tablePostfix) {
        return init(BLANK_STR, modelClass, tablePostfix);
    }

    public FS<T> init(String tablePrefix, Class<T> modelClass, String tablePostfix) {
        return parse(tablePrefix, modelClass, tablePostfix);
    }

    protected String getDefaultTableName() {
        return preEscape + tablePrefix + ModelCacheUtil.getModelFabric(modelClass).getTableName() + tablePostfix
                + postEscape;
    }

    private FS<T> parse(String tablePrefix, Class<T> modelClass, String tablePostfix) {

        this.tablePrefix = tablePrefix == null ? BLANK_STR : tablePrefix.trim();
        this.tablePostfix = tablePostfix == null ? BLANK_STR : tablePostfix.trim();

        String[] escapes = getEscape();
        this.preEscape = escapes[0];
        this.postEscape = escapes[1];
        this.modelClass = modelClass;

        this.tableName = getDefaultTableName();

        PrimaryKey pk = ModelCacheUtil.getModelFabric(modelClass).getPrimaryKey();
        this.primaryKeyColumn = pk == null ? new String[] { "id" } : pk.value();
        ModelFabric mf = ModelCacheUtil.getModelFabric(modelClass);

        this.attrs = mf.getAttrMapping();

        if (this.attrs == null) {
            this.attrs = new HashMap<>();
        }

        this.typeHandlerMap = mf.getTypeHandlerMap();
        if (this.typeHandlerMap == null) {
            this.typeHandlerMap = new HashMap<>();
        }

        return this;
    }

    public FS<T> exclude(String... columnNames) {

        if (columnNames == null) {
            throw new HORMException("exclude columnNames is null");
        }

        List<String> columnExcludeList = new ArrayList<>();
        for (int i = 0; i < columnNames.length; ++i) {
            columnExcludeList.add(columnNames[i]);
            columnExcludeList.add(mapColumnForExclude(columnNames[i]));
        }

        String[] tempColumnNames = new String[columnExcludeList.size()];
        columnExcludeList.toArray(tempColumnNames);

        Collections.addAll(excludedColumns, tempColumnNames);
        return this;
    }

    @SafeVarargs
    public final <R> FS<T> exclude(TypeFunction<T, R>... lambdaArray) {
        String[] columnNames = new String[lambdaArray.length];
        for (int i = 0; i < lambdaArray.length; ++i) {
            columnNames[i] = LambdaUtil.getLambdaFieldName(lambdaArray[i]);
        }
        return exclude(columnNames);
    }

    public FS<T> select(String columns) {
        if (null != this.selectColumns) {
            throw new HORMException("Select method can only be called once.");
        }
        this.selectColumns = mapColumns(columns);
        return this;
    }

    /**
     * 使用native sql表达式， 不要用 ',' 号结束
     * 
     * @param exp
     * @return
     */
    public FS<T> selectExp(String exp) {
        if (null != this.selectExpressionColumns) {
            throw new HORMException("SelectExp method can only be called once.");
        }
        this.selectExpressionColumns = exp;
        return this;
    }

    public FS<T> select(String... columnArray) {
        if (null != this.selectColumns) {
            throw new HORMException("Select method can only be called once.");
        }
        this.selectColumns = mapColumns(columnArray);
        return this;
    }

    @SafeVarargs
    public final <R> FS<T> select(TypeFunction<T, R>... lambdaArray) {
        String[] columnNames = new String[lambdaArray.length];
        for (int i = 0; i < lambdaArray.length; ++i) {
            columnNames[i] = LambdaUtil.getLambdaFieldName(lambdaArray[i]);
        }
        return select(columnNames);
    }

    /**
     * 放在sql最前面的提示
     * 
     * @param hint
     * @return
     */
    public FS<T> hint(String hint) {
        this.hint = hint;
        return this;
    }

    public FS<T> distinct() {
        this.distinct = true;
        return this;
    }

    public FS<T> count() {
        this.count = true;
        return this;
    }

    public FS<T> singleField(String column) {
        this.select(column);
        this.singleField = true;
        return this;
    }

    public <R> FS<T> singleField(TypeFunction<T, R> lambda) {
        return singleField(LambdaUtil.getLambdaFieldName(lambda));
    }

    private String mapColumns(String[] columnArray) {

        this.beforeCheck();
        if (columnArray == null || columnArray.length == 0) {
            return null;
        }
        StringBuilder mappedClumns = new StringBuilder();
        for (String column : columnArray) {
            mappedClumns.append(mapColumn(column.trim())).append(",");
        }
        mappedClumns.deleteCharAt(mappedClumns.length() - 1);
        return mappedClumns.toString();
    }

    private String mapColumns(String columns) {

        this.beforeCheck();
        if (StrKit.isBlank(columns)) {
            return null;
        }
        StringBuilder mappedClumns = new StringBuilder();
        String[] columnArray = columns.split(",");
        for (String column : columnArray) {
            mappedClumns.append(mapColumn(column.trim())).append(",");
        }
        mappedClumns.deleteCharAt(mappedClumns.length() - 1);
        return mappedClumns.toString();
    }

    public String mapColumn(String column) {
        String trueColumn = attrs.get(column);
        return preEscape + (trueColumn == null ? column : trueColumn) + postEscape;
    }

    private String mapColumnForExclude(String column) {
        String trueColumn = attrs.get(column);
        return trueColumn == null ? column : trueColumn;
    }

    /**
     * where condition
     *
     * @param column
     * @return SqlGenerator
     */
    public FS<T> where(String column) {
        String trueStatement = mapColumn(column);
        conditionSQL.append(AND_STR).append(trueStatement);
        return this;
    }

    /**
     * just where condition
     * 
     * @return
     */
    public FS<T> where() {
        conditionSQL.append(AND_STR);
        return this;
    }

    /**
     * 表达式
     * 
     * @param exp
     * @return
     */
    public FS<T> whereExp(String exp) {
        conditionSQL.append(AND_STR).append(exp);
        return this;
    }

    /**
     * 
     * @param lambda
     * @return
     */
    public <R> FS<T> where(TypeFunction<T, R> lambda) {
        return where(LambdaUtil.getLambdaFieldName(lambda));
    }

    /**
     * where condition, simultaneous setting value
     *
     * @param column
     * 
     * @param value  column name
     * @return SqlGenerator
     */
    public FS<T> where(String column, Object value) {
        String trueStatement = mapColumn(column) + " = ?";
        conditionSQL.append(AND_STR).append(trueStatement);
        paramValues.add(convertValue(column, value));
        return this;
    }

    /**
     * 
     * @param lambda
     * @param value
     * @return
     */
    public <R> FS<T> where(TypeFunction<T, R> lambda, Object value) {
        return where(LambdaUtil.getLambdaFieldName(lambda), value);
    }

    /**
     * Set the where parameter according to model, and generate sql like where where
     * age = ? and name = ?
     *
     * @param model
     * @return SqlGenerator
     */
    public FS<T> where(T model) {

        ModelFabric mf = ModelCacheUtil.getModelFabric(model.getClass());
        List<Field> declaredFields = mf.getFields();
        Map<String, String> attrs = mf.getAttrMapping();

        try {
            for (Field declaredField : declaredFields) {

                Object value = declaredField.get(model);

                if (null == value) {
                    continue;
                }
                if (declaredField.getType().equals(String.class) && StrKit.isBlank(value.toString())) {
                    continue;
                }

                String columnName = attrs.get(declaredField.getName());
                this.where(columnName, value);
            }
        } catch (IllegalArgumentException | IllegalAccessException e) {
            GLog.error("where model error:", e);
        }

        return this;
    }

    /**
     * Equals statement
     *
     * @param value column value
     * @return SqlGenerator
     */
    public FS<T> eq(Object value) {
        conditionSQL.append(" = ?");
        paramValues.add(value);
        return this;
    }

    /**
     * generate "IS NOT NULL" statement
     *
     * @return SqlGenerator
     */
    public FS<T> notNull() {
        conditionSQL.append(" IS NOT NULL");
        return this;
    }

    /**
     * generate AND statement, simultaneous setting value
     *
     * @param column
     * @param value  column value
     * @return
     */
    public FS<T> and(String column, Object value) {
        return this.where(column, value);
    }

    /**
     * 
     * @param lambda
     * @param value
     * @return
     */
    public <R> FS<T> and(TypeFunction<T, R> lambda, Object value) {
        return and(LambdaUtil.getLambdaFieldName(lambda), value);
    }

    /**
     * 
     * @param column
     * @return
     */
    public FS<T> and(String column) {
        return this.where(column);
    }

    /**
     * 
     * @param lambda
     * @return
     */
    public <R> FS<T> and(TypeFunction<T, R> lambda) {
        return this.and(LambdaUtil.getLambdaFieldName(lambda));
    }

    /**
     * generate OR statement, simultaneous setting value
     *
     * @param column
     * 
     * @param value  column value
     * @return SqlGenerator
     */
    public FS<T> or(String column, Object value) {

        String trueStatement = mapColumn(column) + " = ?";
        conditionSQL.append(" OR (").append(trueStatement);
        conditionSQL.append(')');
        paramValues.add(convertValue(column, value));
        return this;
    }

    /**
     * 
     * @param lambda
     * @param value
     * @return
     */
    public <R> FS<T> or(TypeFunction<T, R> lambda, Object value) {
        return or(LambdaUtil.getLambdaFieldName(lambda), value);
    }

    /**
     * generate "!=" statement, simultaneous setting value
     *
     * @param column column name [sql]
     * @param value      column value
     * @return SqlGenerator
     */
    public FS<T> andNotEq(String column, Object value) {
        conditionSQL.append(AND_STR).append(mapColumn(column)).append(" != ?");
        paramValues.add(convertValue(column, value));
        return this;
    }

    /**
     * 
     * @param lambda
     * @param value
     * @return
     */
    public <R> FS<T> andNotEq(TypeFunction<T, R> lambda, Object value) {
        return andNotEq(LambdaUtil.getLambdaFieldName(lambda), value);
    }

    /**
     * generate "!=" statement, simultaneous setting value
     *
     * @param value column value
     * @return SqlGenerator
     */
    public FS<T> notEq(Object value) {
        conditionSQL.append(" != ?");
        paramValues.add(value);
        return this;
    }

    /**
     * generate "!= ''" statement
     *
     * @param columnName column name
     * @return SqlGenerator
     */
    public FS<T> andNotEmpty(String columnName) {
        conditionSQL.append(AND_STR).append(mapColumn(columnName)).append(" != ''");
        return this;
    }

    /**
     * 
     * @param columnName
     * @return
     */
    public FS<T> notEmpty(String columnName) {
        conditionSQL.append(mapColumn(columnName)).append(" != ''");
        return this;
    }

    /**
     * generate "!= ''" statement
     *
     * @return
     */
    public FS<T> notEmpty() {
        conditionSQL.append(" != ''");
        return this;
    }

    /**
     * generate "IS NOT NULL" statement
     *
     * @param columnName column name
     * @return
     */
    public FS<T> andNotNull(String columnName) {
        conditionSQL.append(AND_STR).append(mapColumn(columnName)).append(" IS NOT NULL");
        return this;
    }

    /**
     * 
     * @param lambda
     * @return
     */
    public <R> FS<T> andNotNull(TypeFunction<T, R> lambda) {
        return andNotNull(LambdaUtil.getLambdaFieldName(lambda));
    }

    /**
     * generate "IS NOT NULL" statement
     *
     * @param columnName column name
     * @return
     */
    public FS<T> notNull(String columnName) {
        conditionSQL.append(mapColumn(columnName)).append(" IS NOT NULL");
        return this;
    }

    public FS<T> isNull() {
        conditionSQL.append(" IS NULL");
        return this;
    }

    public FS<T> isNull(String columnName) {
        conditionSQL.append(mapColumn(columnName)).append(" IS NULL");
        return this;
    }

    public FS<T> andIsNull(String columnName) {
        conditionSQL.append(AND_STR).append(mapColumn(columnName)).append(" IS NULL");
        return this;
    }

    public <R> FS<T> andIsNull(TypeFunction<T, R> lambda) {
        return andIsNull(LambdaUtil.getLambdaFieldName(lambda));
    }

    public <R> FS<T> isNull(TypeFunction<T, R> lambda) {
        return isNull(LambdaUtil.getLambdaFieldName(lambda));
    }

    /**
     * 默认使用Mysql limit语法
     * 
     * @param size
     * @return
     */
    public FS<T> limit(int size) {
        return limit(0, size);
    }

    /**
     * 默认使用Mysql limit语法
     * 
     * @param offset
     * @param size
     * @return
     */
    public FS<T> limit(int offset, int size) {
        if (limitSQL.length() == 0) {
            limitSQL.append(" LIMIT ").append(offset).append(", ").append(size);
        }
        return this;
    }

    /**
     * 
     * @param lambda
     * @return
     */
    public <R> FS<T> notNull(TypeFunction<T, R> lambda) {
        return notNull(LambdaUtil.getLambdaFieldName(lambda));
    }

    /**
     * generate like statement, simultaneous setting value
     *
     * @param columnName column name
     * @param value      column value
     * @return SqlGenerator
     */
    public FS<T> andLike(String columnName, Object value) {
        conditionSQL.append(AND_STR).append(mapColumn(columnName)).append(" LIKE ?");
        paramValues.add("%" + value + "%");
        return this;
    }

    /**
     * 
     * @param lambda
     * @param value
     * @return
     */
    public <R> FS<T> andLike(TypeFunction<T, R> lambda, Object value) {
        return andLike(LambdaUtil.getLambdaFieldName(lambda), value);
    }

    /**
     * generate like statement, simultaneous setting value
     *
     * @param value column value
     * @return SqlGenerator
     */
    public FS<T> like(Object value) {
        conditionSQL.append(" LIKE ?");
        paramValues.add("%" + value + "%");
        return this;
    }

    /**
     * generate like statement, simultaneous setting value
     *
     * @param columnName column name
     * @param value      column value
     * @return SqlGenerator
     */
    public FS<T> like(String columnName, Object value) {
        String trueColumnName = mapColumn(columnName);
        conditionSQL.append(trueColumnName).append(" LIKE ?");
        paramValues.add("%" + value + "%");
        return this;
    }

    /**
     * 
     * @param lambda
     * @param value
     * @return
     */
    public <R> FS<T> like(TypeFunction<T, R> lambda, Object value) {
        return like(LambdaUtil.getLambdaFieldName(lambda), value);
    }

    /**
     * 
     * @param columnName
     * @param value
     * @return
     */
    public FS<T> andLikeLeft(String columnName, Object value) {
        String trueColumnName = mapColumn(columnName);
        conditionSQL.append(AND_STR).append(trueColumnName).append(" LIKE ?");
        paramValues.add("%" + value);
        return this;
    }

    /**
     * 
     * @param lambda
     * @param value
     * @return
     */
    public <R> FS<T> andLikeLeft(TypeFunction<T, R> lambda, Object value) {
        return andLikeLeft(LambdaUtil.getLambdaFieldName(lambda), value);
    }

    /**
     * 
     * @param value
     * @return
     */
    public FS<T> likeLeft(Object value) {
        conditionSQL.append(" LIKE ?");
        paramValues.add("%" + value);
        return this;
    }

    /**
     * 
     * @param columnName
     * @param value
     * @return
     */
    public FS<T> likeLeft(String columnName, Object value) {
        conditionSQL.append(mapColumn(columnName)).append(" LIKE ?");
        paramValues.add("%" + value);
        return this;
    }

    /**
     * 
     * @param lambda
     * @param value
     * @return
     */
    public <R> FS<T> likeLeft(TypeFunction<T, R> lambda, Object value) {
        return likeLeft(LambdaUtil.getLambdaFieldName(lambda), value);
    }

    /**
     * 
     * @param columnName
     * @param value
     * @return
     */
    public FS<T> andLikeRight(String columnName, Object value) {
        String trueColumnName = mapColumn(columnName);
        conditionSQL.append(AND_STR).append(trueColumnName).append(" LIKE ?");
        paramValues.add(value + "%");
        return this;
    }

    /**
     * 
     * @param lambda
     * @param value
     * @return
     */
    public <R> FS<T> andLikeRight(TypeFunction<T, R> lambda, Object value) {
        return andLikeRight(LambdaUtil.getLambdaFieldName(lambda), value);
    }

    /**
     * 
     * @param value
     * @return
     */
    public FS<T> likeRight(Object value) {
        conditionSQL.append(" LIKE ?");
        paramValues.add(value + "%");
        return this;
    }

    /**
     * 
     * @param columnName
     * @param value
     * @return
     */
    public FS<T> likeRight(String columnName, Object value) {
        conditionSQL.append(mapColumn(columnName)).append(" LIKE ?");
        paramValues.add(value + "%");
        return this;
    }

    /**
     * 
     * @param lambda
     * @param value
     * @return
     */
    public <R> FS<T> likeRight(TypeFunction<T, R> lambda, Object value) {
        return likeRight(LambdaUtil.getLambdaFieldName(lambda), value);
    }

    /**
     * generate between statement, simultaneous setting value
     *
     * @param columnName column name
     * @param a          first range value
     * @param b          second range value
     * @return SqlGenerator
     */
    public FS<T> andBetween(String columnName, Object a, Object b) {
        conditionSQL.append(AND_STR).append(mapColumn(columnName)).append(" BETWEEN ? and ?");
        paramValues.add(convertValue(columnName, a));
        paramValues.add(convertValue(columnName, b));
        return this;
    }

    /**
     * not between
     * 
     * @param columnName
     * @param a
     * @param b
     * @return
     */
    public FS<T> andNotBetween(String columnName, Object a, Object b) {
        conditionSQL.append(AND_STR).append(mapColumn(columnName)).append(" NOT BETWEEN ? and ?");
        paramValues.add(convertValue(columnName, a));
        paramValues.add(convertValue(columnName, b));
        return this;
    }

    /**
     * generate between statement with lambda, simultaneous setting value
     *
     * @param lambda column name with lambda
     * @param a        first range value
     * @param b        second range value
     * @param <R>
     * @return SqlGenerator
     */
    public <R> FS<T> andBetween(TypeFunction<T, R> lambda, Object a, Object b) {
        return andBetween(LambdaUtil.getLambdaFieldName(lambda), a, b);
    }

    /**
     * not between
     * 
     * @param lambda
     * @param a
     * @param b
     * @return
     */
    public <R> FS<T> andNotBetween(TypeFunction<T, R> lambda, Object a, Object b) {
        return andNotBetween(LambdaUtil.getLambdaFieldName(lambda), a, b);
    }

    /**
     * generate between values
     *
     * @param a first range value
     * @param b second range value
     * @return SqlGenerator
     */
    public FS<T> between(Object a, Object b) {
        conditionSQL.append(" BETWEEN ? and ?");
        paramValues.add(a);
        paramValues.add(b);
        return this;
    }

    /**
     * not between
     * 
     * @param a
     * @param b
     * @return
     */
    public FS<T> notBetween(Object a, Object b) {
        conditionSQL.append(" NOT BETWEEN ? and ?");
        paramValues.add(a);
        paramValues.add(b);
        return this;
    }

    /**
     * generate between statement, simultaneous setting value
     *
     * @param columnName column name
     * @param a          first range value
     * @param b          second range value
     * @return SqlGenerator
     */
    public FS<T> between(String columnName, Object a, Object b) {
        conditionSQL.append(mapColumn(columnName)).append(" BETWEEN ? and ?");
        paramValues.add(convertValue(columnName, a));
        paramValues.add(convertValue(columnName, b));
        return this;
    }

    /**
     * not between
     * 
     * @param columnName
     * @param a
     * @param b
     * @return
     */
    public FS<T> notBetween(String columnName, Object a, Object b) {
        conditionSQL.append(mapColumn(columnName)).append(" NOT BETWEEN ? and ?");
        paramValues.add(convertValue(columnName, a));
        paramValues.add(convertValue(columnName, b));
        return this;
    }

    /**
     * generate between statement with lambda, simultaneous setting value
     *
     * @param lambda column name with lambda
     * @param a        first range value
     * @param b        second range value
     * @param <R>
     * @return SqlGenerator
     */
    public <R> FS<T> between(TypeFunction<T, R> lambda, Object a, Object b) {
        return between(LambdaUtil.getLambdaFieldName(lambda), a, b);
    }

    /**
     * not between
     * 
     * @param lambda
     * @param a
     * @param b
     * @return
     */
    public <R> FS<T> notBetween(TypeFunction<T, R> lambda, Object a, Object b) {
        return notBetween(LambdaUtil.getLambdaFieldName(lambda), a, b);
    }

    /**
     * generate ">" statement, simultaneous setting value
     *
     * @param columnName table column name [sql]
     * @param value      column value
     * @return SqlGenerator
     */
    public FS<T> andGt(String columnName, Object value) {
        String trueColumnName = mapColumn(columnName);
        conditionSQL.append(AND_STR).append(trueColumnName).append(" > ?");
        paramValues.add(convertValue(columnName, value));
        return this;
    }

    /**
     * generate ">" statement with lambda, simultaneous setting value
     *
     * @param lambda column name with lambda
     * @param value    column value
     * @param <R>
     * @return SqlGenerator
     */
    public <R> FS<T> andGt(TypeFunction<T, R> lambda, Object value) {
        return andGt(LambdaUtil.getLambdaFieldName(lambda), value);
    }

    /**
     * generate ">" statement value
     *
     * @param value column value
     * @return SqlGenerator
     */
    public FS<T> gt(Object value) {
        conditionSQL.append(" > ?");
        paramValues.add(value);
        return this;
    }

    /**
     * generate ">" statement, simultaneous setting value
     *
     * @param columnName table column name [sql]
     * @param value      column value
     * @return SqlGenerator
     */
    public FS<T> gt(String columnName, Object value) {
        String trueColumnName = mapColumn(columnName);
        conditionSQL.append(trueColumnName).append(" > ?");
        paramValues.add(convertValue(columnName, value));
        return this;
    }

    /**
     * generate ">" statement with lambda, simultaneous setting value
     *
     * @param lambda column name with lambda
     * @param value    column value
     * @param <R>
     * @return SqlGenerator
     */
    public <R> FS<T> gt(TypeFunction<T, R> lambda, Object value) {
        return gt(LambdaUtil.getLambdaFieldName(lambda), value);
    }

    /**
     * generate ">=" statement, simultaneous setting value
     *
     * @param column table column name [sql]
     * @param value  column value
     * @return SqlGenerator
     */
    public FS<T> andGte(String column, Object value) {
        String trueColumnName = mapColumn(column);
        conditionSQL.append(AND_STR).append(trueColumnName).append(" >= ?");
        paramValues.add(convertValue(column, value));
        return this;
    }

    /**
     * generate ">=" statement, simultaneous setting value
     * 
     * @param lambda table column name with lambda
     * @param value
     * @return
     */
    public <R> FS<T> andGte(TypeFunction<T, R> lambda, Object value) {
        return andGte(LambdaUtil.getLambdaFieldName(lambda), value);
    }

    /**
     * generate ">=" statement value
     *
     * @param value column value
     * @return SqlGenerator
     */
    public FS<T> gte(Object value) {
        conditionSQL.append(" >= ?");
        paramValues.add(value);
        return this;
    }

    /**
     * generate "<" statement, simultaneous setting value
     *
     * @param column table column name [sql]
     * @param value  column value
     * @return SqlGenerator
     */
    public FS<T> andLt(String column, Object value) {
        String trueColumnName = mapColumn(column);
        conditionSQL.append(AND_STR).append(trueColumnName).append(" < ?");
        paramValues.add(value);
        return this;
    }

    /**
     * generate "<" statement, simultaneous setting value
     * 
     * @param lambda
     * @param value
     * @return
     */
    public <R> FS<T> andLt(TypeFunction<T, R> lambda, Object value) {
        return andLt(LambdaUtil.getLambdaFieldName(lambda), value);
    }

    /**
     * generate "<" statement value
     *
     * @param value column value
     * @return SqlGenerator
     */
    public FS<T> lt(Object value) {
        conditionSQL.append(" < ?");
        paramValues.add(value);
        return this;
    }

    /**
     * generate "<=" statement, simultaneous setting value
     *
     * @param column table column name [sql]
     * @param value  column value
     * @return SqlGenerator
     */
    public FS<T> andLte(String column, Object value) {
        String trueColumnName = mapColumn(column);
        conditionSQL.append(AND_STR).append(trueColumnName).append(" <= ?");
        paramValues.add(value);
        return this;
    }

    /**
     * generate "<=" statement, simultaneous setting value
     * 
     * @param lambda
     * @param value
     * @return
     */
    public <R> FS<T> andLte(TypeFunction<T, R> lambda, Object value) {
        return andLte(LambdaUtil.getLambdaFieldName(lambda), value);
    }

    /**
     * generate "<=" statement value
     *
     * @param value column value
     * @return SqlGenerator
     */
    public FS<T> lte(Object value) {
        conditionSQL.append(" <= ?");
        paramValues.add(value);
        return this;
    }

    /**
     * generate ">=" statement, simultaneous setting value
     *
     * @param column table column name [sql]
     * @param value  column value
     * @return SqlGenerator
     */
    public FS<T> gte(String column, Object value) {
        conditionSQL.append(mapColumn(column)).append(" >= ?");
        paramValues.add(value);
        return this;
    }

    /**
     * generate ">=" statement, simultaneous setting value
     * 
     * @param lambda table column name with lambda
     * @param value
     * @return
     */
    public <R> FS<T> gte(TypeFunction<T, R> lambda, Object value) {
        return gte(LambdaUtil.getLambdaFieldName(lambda), value);
    }

    /**
     * generate "<" statement, simultaneous setting value
     *
     * @param column table column name [sql]
     * @param value  column value
     * @return SqlGenerator
     */
    public FS<T> lt(String column, Object value) {
        String trueColumnName = mapColumn(column);
        conditionSQL.append(trueColumnName).append(" < ?");
        paramValues.add(value);
        return this;
    }

    /**
     * generate "<" statement, simultaneous setting value
     * 
     * @param lambda
     * @param value
     * @return
     */
    public <R> FS<T> lt(TypeFunction<T, R> lambda, Object value) {
        return lt(LambdaUtil.getLambdaFieldName(lambda), value);
    }

    /**
     * generate "<=" statement, simultaneous setting value
     *
     * @param column table column name [sql]
     * @param value  column value
     * @return SqlGenerator
     */
    public FS<T> lte(String column, Object value) {
        String trueColumnName = mapColumn(column);
        conditionSQL.append(trueColumnName).append(" <= ?");
        paramValues.add(value);
        return this;
    }

    /**
     * generate "<=" statement, simultaneous setting value
     * 
     * @param lambda
     * @param value
     * @return
     */
    public <R> FS<T> lte(TypeFunction<T, R> lambda, Object value) {
        return lte(LambdaUtil.getLambdaFieldName(lambda), value);
    }

    /**
     * generate "in" statement, simultaneous setting value
     *
     * @param column table column name [sql]
     * @param args   column value
     * @return SqlGenerator
     */
    public FS<T> andIn(String column, Object... args) {
        if (null == args || args.length == 0) {
            GLog.error("column:{}, in params is empty.", column);
            return this;
        }
        String trueColumnName = mapColumn(column);
        conditionSQL.append(AND_STR).append(trueColumnName).append(" IN (");
        this.setArguments(args);
        conditionSQL.append(")");
        return this;
    }

    /**
     * not in
     * 
     * @param column
     * @param args
     * @return
     */
    public FS<T> andNotIn(String column, Object... args) {
        if (null == args || args.length == 0) {
            GLog.error("column:{}, in params is empty.", column);
            return this;
        }
        String trueColumnName = mapColumn(column);
        conditionSQL.append(AND_STR).append(trueColumnName).append(" NOT IN (");
        this.setArguments(args);
        conditionSQL.append(")");
        return this;
    }

    /**
     * generate "in" statement, simultaneous setting value
     *
     * @param column table column name [sql]
     * @param args   column value
     * @return SqlGenerator
     */
    public FS<T> in(String column, Object... args) {
        if (null == args || args.length == 0) {
            GLog.error("column:{}, in params is empty.", column);
            return this;
        }
        String trueColumnName = mapColumn(column);
        conditionSQL.append(trueColumnName).append(" IN (");
        this.setArguments(args);
        conditionSQL.append(")");
        return this;
    }

    /**
     * not in
     * 
     * @param column
     * @param args
     * @return
     */
    public FS<T> notIn(String column, Object... args) {
        if (null == args || args.length == 0) {
            GLog.error("column:{}, in params is empty.", column);
            return this;
        }
        String trueColumnName = mapColumn(column);
        conditionSQL.append(trueColumnName).append(" NOT IN (");
        this.setArguments(args);
        conditionSQL.append(")");
        return this;
    }

    /**
     * 
     * @param lambda
     * @param args
     * @return
     */
    public <R> FS<T> in(TypeFunction<T, R> lambda, Object... args) {
        return in(LambdaUtil.getLambdaFieldName(lambda), args);
    }

    /**
     * not in
     * 
     * @param lambda
     * @param args
     * @return
     */
    public <R> FS<T> notIn(TypeFunction<T, R> lambda, Object... args) {
        return notIn(LambdaUtil.getLambdaFieldName(lambda), args);
    }

    /**
     * 
     * @param lambda
     * @param args
     * @return
     */
    public <R> FS<T> andIn(TypeFunction<T, R> lambda, Object... args) {
        return andIn(LambdaUtil.getLambdaFieldName(lambda), args);
    }

    /**
     * and not in
     * 
     * @param lambda
     * @param args
     * @return
     */
    public <R> FS<T> andNotIn(TypeFunction<T, R> lambda, Object... args) {
        return andNotIn(LambdaUtil.getLambdaFieldName(lambda), args);
    }

    /**
     * generate "in" statement value
     *
     * @param args column value
     * @return SqlGenerator
     */
    public FS<T> in(Object... args) {
        if (null == args || args.length == 0) {
            GLog.error("Column: {}, query params is empty.");
            return this;
        }
        conditionSQL.append(" IN (");
        this.setArguments(args);
        conditionSQL.append(")");
        return this;
    }

    /**
     * not in
     * 
     * @param args
     * @return
     */
    public FS<T> notIn(Object... args) {
        if (null == args || args.length == 0) {
            GLog.error("Column: {}, query params is empty.");
            return this;
        }
        conditionSQL.append(" NOT IN (");
        this.setArguments(args);
        conditionSQL.append(")");
        return this;
    }

    /**
     * Set in params
     *
     * @param list in param values
     * @param <S>
     * @return SqlGenerator
     */
    public <S> FS<T> in(List<S> list) {
        return this.in(list.toArray());
    }

    /**
     * not in
     * 
     * @param list
     * @return
     */
    public <S> FS<T> notIn(List<S> list) {
        return this.notIn(list.toArray());
    }

    /**
     * generate "in" statement, simultaneous setting value
     *
     * @param column column name
     * @param args   in param values
     * @param <S>
     * @return SqlGenerator
     */
    public <S> FS<T> in(String column, List<S> args) {
        return this.in(column, args.toArray());
    }

    /**
     * not in
     * 
     * @param column
     * @param args
     * @return
     */
    public <S> FS<T> notIn(String column, List<S> args) {
        return this.notIn(column, args.toArray());
    }

    /**
     * 
     * @param lambda
     * @param args
     * @return
     */
    public <R, S> FS<T> in(TypeFunction<T, R> lambda, List<S> args) {
        return this.in(LambdaUtil.getLambdaFieldName(lambda), args);
    }

    /**
     * not in
     * 
     * @param lambda
     * @param args
     * @return
     */
    public <R, S> FS<T> notIn(TypeFunction<T, R> lambda, List<S> args) {
        return this.notIn(LambdaUtil.getLambdaFieldName(lambda), args);
    }

    /**
     * generate "in" statement, simultaneous setting value
     *
     * @param column column name
     * @param args   in param values
     * @param <S>
     * @return SqlGenerator
     */
    public <S> FS<T> andIn(String column, List<S> args) {
        return this.andIn(column, args.toArray());
    }

    /**
     * and not in
     * 
     * @param column
     * @param args
     * @return
     */
    public <S> FS<T> andNotIn(String column, List<S> args) {
        return this.andNotIn(column, args.toArray());
    }

    /**
     * 
     * @param lambda
     * @param args
     * @return
     */
    public <R, S> FS<T> andIn(TypeFunction<T, R> lambda, List<S> args) {
        return this.andIn(LambdaUtil.getLambdaFieldName(lambda), args);
    }

    /**
     * and not in
     * 
     * @param lambda
     * @param args
     * @return
     */
    public <R, S> FS<T> andNotIn(TypeFunction<T, R> lambda, List<S> args) {
        return this.andNotIn(LambdaUtil.getLambdaFieldName(lambda), args);
    }

    /**
     * generate order by statement
     *
     * @param order like "id desc"
     * @return SqlGenerator
     */
    public FS<T> order(String order) {
        if (StrKit.isBlank(order)) {
            throw new HORMException("order is empty");
        }

        if (this.orderBySQL.length() > 0) {
            this.orderBySQL.append(',');
        }

        String orderTemp = order.trim();
        String newOrder = orderTemp;
        String column = null;
        String flag = null;

        int index = orderTemp.indexOf(" ");
        if (index > -1) {
            column = orderTemp.substring(0, index);
            flag = orderTemp.substring(index);
            newOrder = mapColumn(column) + " " + flag;
        }

        this.orderBySQL.append(' ').append(newOrder);
        return this;
    }

    /**
     * generate order by statement
     *
     * @param columnName column name
     * @param orderBy    order by @see OrderBy
     * @return SqlGenerator
     */
    public FS<T> order(String columnName, OrderBy orderBy) {
        if (this.orderBySQL.length() > 0) {
            this.orderBySQL.append(',');
        }
        String trueColumnName = mapColumn(columnName);
        this.orderBySQL.append(' ').append(trueColumnName).append(' ').append(orderBy.toString());
        return this;
    }

    /**
     * group by
     * 
     * @param exps
     * @return
     */
    public FS<T> groupByExp(String exps) {

        if (this.groupBySQL.length() > 0) {
            throw new HORMException("group by sql can only be set once.");
        }

        if (exps == null) {
            throw new HORMException("groupBy 参数不能为空");
        }

        groupBySQL.append(' ').append(exps);
        return this;
    }

    /**
     * 添加group by
     * 
     * @param columnNames
     * @return
     */
    public FS<T> groupBy(String... columnNames) {

        if (this.groupBySQL.length() > 0) {
            throw new HORMException("group by sql can only be set once.");
        }

        if (columnNames == null || columnNames.length == 0) {
            throw new HORMException("groupBy 参数不能为空");
        }

        for (String columnName : columnNames) {
            String trueColumnName = mapColumn(columnName);
            groupBySQL.append(' ').append(trueColumnName).append(",");
        }
        groupBySQL.deleteCharAt(groupBySQL.length() - 1);
        return this;
    }

    /**
     * 添加group by
     * 
     * @param <R>
     * @param lambdaArray
     * @return
     */
    @SafeVarargs
    public final <R> FS<T> groupBy(TypeFunction<T, R>... lambdaArray) {
        String[] columnNames = new String[lambdaArray.length];
        for (int i = 0; i < lambdaArray.length; ++i) {
            columnNames[i] = LambdaUtil.getLambdaFieldName(lambdaArray[i]);
        }
        return groupBy(columnNames);
    }

    /**
     * 
     * @param lambda
     * @param orderBy
     * @return
     */
    public <R> FS<T> order(TypeFunction<T, R> lambda, OrderBy orderBy) {
        return order(LambdaUtil.getLambdaFieldName(lambda), orderBy);
    }

    /**
     * Update columns set value
     *
     * @param column column name
     * @param value  column value
     * @return SqlGenerator
     */
    public FS<T> set(String column, Object value) {
        String trueColumnName = mapColumn(column);
        updateColumns.put(trueColumnName, convertValue(column, value));
        return this;
    }

    /**
     * 更新字段自增
     * 
     * @param column
     * @param count
     * @return
     */
    public FS<T> inc(String column, Number count) {
        String trueColumnName = mapColumn(column);
        String exp = trueColumnName + " + " + count;
        setExp(trueColumnName, exp);
        return this;
    }

    /**
     * 更新字段自增
     * 
     * @param lambda
     * @param count
     * @return
     */
    public <R> FS<T> inc(TypeFunction<T, R> lambda, Number count) {
        return inc(LambdaUtil.getLambdaFieldName(lambda), count);
    }

    /**
     * 更新字段自减
     * 
     * @param column
     * @param count
     * @return
     */
    public FS<T> dec(String column, Number count) {
        String trueColumnName = mapColumn(column);
        String exp = trueColumnName + " - " + count;
        setExp(trueColumnName, exp);
        return this;
    }

    /**
     * 更新字段自减
     * 
     * @param lambda
     * @param count
     * @return
     */
    public <R> FS<T> dec(TypeFunction<T, R> lambda, Number count) {
        return dec(LambdaUtil.getLambdaFieldName(lambda), count);
    }

    /**
     * 更新字段自增 1
     * 
     * @param column
     * @return
     */
    public FS<T> inc(String column) {
        return inc(column, 1);
    }

    /**
     * 更新字段自增 1
     * 
     * @param lambda
     * @return
     */
    public <R> FS<T> inc(TypeFunction<T, R> lambda) {
        return inc(LambdaUtil.getLambdaFieldName(lambda));
    }

    /**
     * 更新字段自减 1
     * 
     * @param column
     * @return
     */
    public FS<T> dec(String column) {
        return dec(column, 1);
    }

    /**
     * 更新字段自减 1
     * 
     * @param lambda
     * @return
     */
    public <R> FS<T> dec(TypeFunction<T, R> lambda) {
        return dec(LambdaUtil.getLambdaFieldName(lambda));
    }

    /**
     * 更新字段为native sql
     * 
     * @param column
     * @param exp    native sql表达式
     * @return
     */
    public FS<T> setExp(String column, String exp) {
        String trueColumnName = mapColumn(column);
        updateColumnExpression.put(trueColumnName, exp);
        return this;
    }

    /**
     * 
     * @param lambda
     * @param value
     * @return
     */
    public <R> FS<T> set(TypeFunction<T, R> lambda, Object value) {
        return set(LambdaUtil.getLambdaFieldName(lambda), value);
    }

    /**
     * 更新字段为native sql
     * 
     * @param lambda
     * @param exp
     * @return
     */
    public <R> FS<T> setExp(TypeFunction<T, R> lambda, String exp) {
        return setExp(LambdaUtil.getLambdaFieldName(lambda), exp);
    }

    public FS<T> set(T model) {
        this.model = model;
        return this;
    }

    public FS<T> orNest(Function<FS<T>, FS<T>> func) {
        return or().nest(func);
    }

    public FS<T> andNest(Function<FS<T>, FS<T>> func) {
        return and().nest(func);
    }

    public FS<T> nest(Function<FS<T>, FS<T>> func) {
        conditionSQL.append("(");
        func.apply(this);
        conditionSQL.append(")");
        return this;
    }

    public FS<T> and() {
        conditionSQL.append(AND_STR);
        return this;
    }

    public FS<T> or() {
        conditionSQL.append(" OR ");
        return this;
    }

    public FS<T> eq(String column, Object value) {
        conditionSQL.append(mapColumn(column)).append(" = ?");
        paramValues.add(value);
        return this;
    }

    public <R> FS<T> eq(TypeFunction<T, R> lambda, Object value) {
        return eq(LambdaUtil.getLambdaFieldName(lambda), value);
    }

    public FS<T> andEq(String column, Object value) {
        conditionSQL.append(AND_STR).append(mapColumn(column)).append(" = ?");
        paramValues.add(value);
        return this;
    }

    public <R> FS<T> andEq(TypeFunction<T, R> lambda, Object value) {
        return andEq(LambdaUtil.getLambdaFieldName(lambda), value);
    }

    /////////// NEW API /////////
    public FS<T> ltMeta(String column) {
        conditionSQL.append(" < ").append(mapColumn(column));
        return this;
    }

    public <R> FS<T> ltMeta(TypeFunction<T, R> lambda) {
        return ltMeta(LambdaUtil.getLambdaFieldName(lambda));
    }

    /**
     * 条件添加表达式
     * 
     * @param exp native sql expression
     * @return
     */
    public FS<T> exp(String exp) {
        conditionSQL.append(exp);
        return this;
    }

    /**
     * 条件添加表达式
     * 
     * @param exp native sql expression
     * @return
     */
    public FS<T> andExp(String exp) {
        conditionSQL.append(AND_STR).append(exp);
        return this;
    }

    /**
     * 
     * @param exp native sql expression
     * @return
     */
    public FS<T> ltExp(String exp) {
        conditionSQL.append(" < ").append(exp);
        return this;
    }

    public FS<T> lteMeta(String column) {
        conditionSQL.append(" <= ").append(mapColumn(column));
        return this;
    }

    public <R> FS<T> lteMeta(TypeFunction<T, R> lambda) {
        return lteMeta(LambdaUtil.getLambdaFieldName(lambda));
    }

    public FS<T> lteExp(String exp) {
        conditionSQL.append(" <= ").append(exp);
        return this;
    }

    public FS<T> ltMeta(String column1, String column2) {
        conditionSQL.append(mapColumn(column1)).append(" < ").append(mapColumn(column2));
        return this;
    }

    public FS<T> andLtMeta(String column1, String column2) {
        conditionSQL.append(AND_STR).append(mapColumn(column1)).append(" < ").append(mapColumn(column2));
        return this;
    }

    public <R> FS<T> ltMeta(TypeFunction<T, R> lambda1, TypeFunction<T, R> lambda2) {
        return ltMeta(LambdaUtil.getLambdaFieldName(lambda1), LambdaUtil.getLambdaFieldName(lambda2));
    }

    public <R> FS<T> andLtMeta(TypeFunction<T, R> lambda1, TypeFunction<T, R> lambda2) {
        return andLtMeta(LambdaUtil.getLambdaFieldName(lambda1), LambdaUtil.getLambdaFieldName(lambda2));
    }

    public FS<T> lteMeta(String column1, String column2) {
        conditionSQL.append(mapColumn(column1)).append(" <= ").append(mapColumn(column2));
        return this;
    }

    public FS<T> andLteMeta(String column1, String column2) {
        conditionSQL.append(AND_STR).append(mapColumn(column1)).append(" <= ").append(mapColumn(column2));
        return this;
    }

    public <R> FS<T> lteMeta(TypeFunction<T, R> lambda1, TypeFunction<T, R> lambda2) {
        return lteMeta(LambdaUtil.getLambdaFieldName(lambda1), LambdaUtil.getLambdaFieldName(lambda2));
    }

    public <R> FS<T> andLteMeta(TypeFunction<T, R> lambda1, TypeFunction<T, R> lambda2) {
        return andLteMeta(LambdaUtil.getLambdaFieldName(lambda1), LambdaUtil.getLambdaFieldName(lambda2));
    }

    /////// NEW API2 //////////
    public FS<T> eqMeta(String column) {
        conditionSQL.append(" = ").append(mapColumn(column));
        return this;
    }

    public FS<T> eqExp(String exp) {
        conditionSQL.append(" = ").append(exp);
        return this;
    }

    public <R> FS<T> eqMeta(TypeFunction<T, R> lambda) {
        return eqMeta(LambdaUtil.getLambdaFieldName(lambda));
    }

    public FS<T> eqMeta(String column1, String column2) {
        conditionSQL.append(mapColumn(column1)).append(" = ").append(mapColumn(column2));
        return this;
    }

    public FS<T> andEqMeta(String column1, String column2) {
        conditionSQL.append(AND_STR).append(mapColumn(column1)).append(" = ").append(mapColumn(column2));
        return this;
    }

    public <R> FS<T> eqMeta(TypeFunction<T, R> lambda1, TypeFunction<T, R> lambda2) {
        return eqMeta(LambdaUtil.getLambdaFieldName(lambda1), LambdaUtil.getLambdaFieldName(lambda2));
    }

    public <R> FS<T> andEqMeta(TypeFunction<T, R> lambda1, TypeFunction<T, R> lambda2) {
        return andEqMeta(LambdaUtil.getLambdaFieldName(lambda1), LambdaUtil.getLambdaFieldName(lambda2));
    }

    ///////// NEW API3 //////////
    public FS<T> gtMeta(String column) {
        conditionSQL.append(" > ").append(mapColumn(column));
        return this;
    }

    public FS<T> gtExp(String exp) {
        conditionSQL.append(" > ").append(exp);
        return this;
    }

    public <R> FS<T> gtMeta(TypeFunction<T, R> lambda) {
        return gtMeta(LambdaUtil.getLambdaFieldName(lambda));
    }

    public FS<T> gtMeta(String column1, String column2) {
        conditionSQL.append(mapColumn(column1)).append(" > ").append(mapColumn(column2));
        return this;
    }

    public FS<T> andGtMeta(String column1, String column2) {
        conditionSQL.append(AND_STR).append(mapColumn(column1)).append(" > ").append(mapColumn(column2));
        return this;
    }

    public <R> FS<T> gtMeta(TypeFunction<T, R> lambda1, TypeFunction<T, R> lambda2) {
        return gtMeta(LambdaUtil.getLambdaFieldName(lambda1), LambdaUtil.getLambdaFieldName(lambda2));
    }

    public <R> FS<T> andGtMeta(TypeFunction<T, R> lambda1, TypeFunction<T, R> lambda2) {
        return andGtMeta(LambdaUtil.getLambdaFieldName(lambda1), LambdaUtil.getLambdaFieldName(lambda2));
    }

    public FS<T> gteMeta(String column) {
        conditionSQL.append(" >= ").append(mapColumn(column));
        return this;
    }

    public FS<T> gteExp(String exp) {
        conditionSQL.append(" >= ").append(exp);
        return this;
    }

    public <R> FS<T> gteMeta(TypeFunction<T, R> lambda) {
        return gteMeta(LambdaUtil.getLambdaFieldName(lambda));
    }

    public FS<T> gteMeta(String column1, String column2) {
        conditionSQL.append(mapColumn(column1)).append(" >= ").append(mapColumn(column2));
        return this;
    }

    public FS<T> andGteMeta(String column1, String column2) {
        conditionSQL.append(AND_STR).append(mapColumn(column1)).append(" >= ").append(mapColumn(column2));
        return this;
    }

    public <R> FS<T> gteMeta(TypeFunction<T, R> lambda1, TypeFunction<T, R> lambda2) {
        return gteMeta(LambdaUtil.getLambdaFieldName(lambda1), LambdaUtil.getLambdaFieldName(lambda2));
    }

    public <R> FS<T> andGteMeta(TypeFunction<T, R> lambda1, TypeFunction<T, R> lambda2) {
        return andGteMeta(LambdaUtil.getLambdaFieldName(lambda1), LambdaUtil.getLambdaFieldName(lambda2));
    }

    public FS<T> notEqMeta(String column) {
        conditionSQL.append(" != ").append(mapColumn(column));
        return this;
    }

    public FS<T> notEqExp(String exp) {
        conditionSQL.append(" != ").append(exp);
        return this;
    }

    public <R> FS<T> notEqMeta(TypeFunction<T, R> lambda) {
        return notEqMeta(LambdaUtil.getLambdaFieldName(lambda));
    }

    public FS<T> notEqMeta(String column1, String column2) {
        conditionSQL.append(mapColumn(column1)).append(" != ").append(mapColumn(column2));
        return this;
    }

    public FS<T> andNotEqMeta(String column1, String column2) {
        conditionSQL.append(AND_STR).append(mapColumn(column1)).append(" != ").append(mapColumn(column2));
        return this;
    }

    public <R> FS<T> notEqMeta(TypeFunction<T, R> lambda1, TypeFunction<T, R> lambda2) {
        return notEqMeta(LambdaUtil.getLambdaFieldName(lambda1), LambdaUtil.getLambdaFieldName(lambda2));
    }

    public <R> FS<T> andNotEqMeta(TypeFunction<T, R> lambda1, TypeFunction<T, R> lambda2) {
        return andNotEqMeta(LambdaUtil.getLambdaFieldName(lambda1), LambdaUtil.getLambdaFieldName(lambda2));
    }

    public FS<T> setNull(String column) {
        return set(column, null);
    }

    public <R> FS<T> setNull(TypeFunction<T, R> lambda) {
        return setNull(LambdaUtil.getLambdaFieldName(lambda));
    }

    /**
     * Update a model
     *
     * @param args
     * @return affect the number of rows
     */

    private void setArguments(Object[] args) {
        for (int i = 0; i < args.length; i++) {
            if (i == args.length - 1) {
                conditionSQL.append("?");
            } else {
                conditionSQL.append("?, ");
            }
            paramValues.add(args[i]);
        }
    }

    /**
     * pre check
     */
    private void beforeCheck() {
        if (null == this.modelClass) {
            throw new HORMException("model calss is null");
        }
    }

    private Object convertValue(TypeHandler<?> typeHandler, Object v) {
        if (typeHandler == null) {
            return v;
        }

        TypedParameter tp = new TypedParameter();
        tp.setParameter(v);
        tp.setTypeHandler(typeHandler);

        return tp;
    }

    private Object convertValue(String column, Object value) {
        return convertValue(typeHandlerMap.get(column), value);
    }

    public Class<T> getModelClass() {
        return modelClass;
    }

    public StringBuilder getConditionSQL() {
        return conditionSQL;
    }

    public StringBuilder getOrderBySQL() {
        return orderBySQL;
    }

    public StringBuilder getGroupBySQL() {
        return groupBySQL;
    }

    public StringBuilder getLimitSQL() {
        return limitSQL;
    }

    public List<String> getExcludedColumns() {
        return excludedColumns;
    }

    public List<Object> getParamValues() {
        return paramValues;
    }

    public Map<String, Object> getUpdateColumns() {
        return updateColumns;
    }

    public Map<String, String> getUpdateColumnExpression() {
        return updateColumnExpression;
    }

    public String getSelectColumns() {
        return selectColumns;
    }

    public String getSelectExpressionColumns() {
        return selectExpressionColumns;
    }

    public boolean isDistinct() {
        return distinct;
    }

    public boolean isCount() {
        return count;
    }

    public String[] getPrimaryKeyColumn() {
        return primaryKeyColumn;
    }

    public String getTableName() {
        return tableName;
    }

    public Map<String, String> getAttrs() {
        return attrs;
    }

    public Map<String, TypeHandler<?>> getTypeHandlerMap() {
        return typeHandlerMap;
    }

    public T getModel() {
        return model;
    }

    public String getPreEscape() {
        return preEscape;
    }

    public String getPostEscape() {
        return postEscape;
    }

    public boolean isSingleField() {
        return singleField;
    }

    public String getHint() {
        return hint;
    }

    public String getTablePostfix() {
        return tablePostfix;
    }

    public String getTablePrefix() {
        return tablePrefix;
    }
}
